package
{
	import jam.objects.Checkpoint;
	import flash.geom.Point;
	import jam.objects.Exit;
	import jam.objects.Line;
	import jam.objects.Downer;
	import jam.objects.Spring;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import org.osmf.traits.PlayState;
	import org.flixel.system.FlxAnim;
	import jam.registry.Resource;
	import flash.display.BitmapData;
	import org.flixel.*;
	
	public class PlayState extends FlxState
	{
		public var needles:FlxGroup;
		public var level:FlxTilemap;
		public var player:Player;
		public var lines:FlxGroup;
		public var downers:FlxGroup;
		public var jumpPads:FlxGroup;
		public var heartRateText:FlxText;
		public var killers:FlxGroup;
		public var deathText:FlxText;
		public var exit:FlxGroup;
		public var heartRate:HeartRateMonitor;
		private var exitText:FlxText;
		private var killedByNeedles:Boolean = false;
		private var downtownString:String;
		private var backgroundImage:FlxSprite;
		private var boss:Boss;
		
		private var dieOD:Boolean = false;
		
		//sound
		private var downtownNoise:FlxSound;
		private var sniffSound:FlxSound;
		private var downerSound:FlxSound;
		private var jumpSound:FlxSound;
		private var springSound:FlxSound;
		
		private var checkPointXY:Point;
		private var checkPoint:FlxGroup;
		private var debugText:FlxText;
		private var levelNumber:uint;
		private var checkPointReached:Boolean;
		
		public function PlayState(level:uint, checkPoint:Boolean = false) {
			levelNumber = level;
			checkPointReached = checkPoint;
		}
		private var howIDiedText:FlxText;
		
		// Setup logic called.
		override public function create():void
		{
			// Sets the background color.
			FlxG.bgColor = 0xffaaaaaa;
 
 			//set background image
 			backgroundImage = new FlxSprite(0, 0);
 			backgroundImage.loadGraphic(Resource.IMAGE_BACKGROUND, false);
           	backgroundImage.scrollFactor.x = backgroundImage.scrollFactor.y = 0.5;
           	backgroundImage.solid = false;
			this.add(backgroundImage);
 
			var level:FlxTilemap = new FlxTilemap();
			var pixels:BitmapData = new FlxSprite(0, 0, Resource['LEVEL'+ levelNumber]).pixels;
			// Call the Flixel bitmapToCSV with the new color map array set to our tiles
			var worldString:String = FlxTilemap.bitmapToCSV(pixels, false, 1);
			// Load the map like normal
			level.loadMap(worldString, FlxTilemap.ImgAuto, 0, 0, FlxTilemap.AUTO);
			add(level);
			
			backgroundImage.scale = new FlxPoint(level.width / backgroundImage.width,
													level.height / backgroundImage.height * 1.2);
			
			//Create and spawn player
			player = new Player(Player.LEVEL_ONE_X, Player.LEVEL_ONE_Y);
			if(levelNumber == 2)
				player = new Player(Player.LEVEL_TWO_X, Player.LEVEL_TWO_Y);
			if(levelNumber == 3)		
				player = new Player(Player.LEVEL_THREE_X, Player.LEVEL_THREE_Y);
			if(levelNumber == 4)
				player = new Player(Player.LEVEL_FOUR_X, Player.LEVEL_FOUR_Y);
			player.deathCallback = deathPowderCallback;
			
			if (Game.DEBUG)
			{
				//Create heart rate text hud
				heartRateText = new FlxText(10, 70, 120);
				heartRateText.text = "Heart Rate: " + '\n' + player.heartRate;
				heartRateText.scrollFactor.x = heartRateText.scrollFactor.y = 0; 
				add(heartRateText);
			
				howIDiedText = new FlxText(FlxG.camera.width / 2, 50, 80);
				howIDiedText.scrollFactor.x = howIDiedText.scrollFactor.y = 0;
				howIDiedText.text = "You will die.";
				howIDiedText.visible = true;
				add(howIDiedText);
			
				debugText = new FlxText(FlxG.camera.width / 2, 2, 80);
				debugText.scrollFactor.x = debugText.scrollFactor.y = 0; 
				debugText.visible = true;
				add(debugText);
			}
			
			//Hud setup
			lines = new FlxGroup();
			downers = new FlxGroup();
			jumpPads = new FlxGroup();
			exit = new FlxGroup();
			needles = new FlxGroup();
			checkPoint = new FlxGroup();
			
			for(var x:int = 0; x < pixels.width; x++) {	
				for(var y:int = 0; y < pixels.height; y++) {
					if( pixels.getPixel(x, y) == 0x00FF00) {
						createLine(x, y);
					}
					if( pixels.getPixel(x, y) == 0x0000FF) {
						createDowner(x, y);
					}
					if( pixels.getPixel(x, y) == 0x00FFFF) {
						createJumpPad(x, y);
					}
					if( pixels.getPixel(x, y) == 0xECFF42) {
						createExit(x, y);
					}
					if( pixels.getPixel(x, y) == 0xFF1900) {
						createNeedle(x, y);
					}
					if( pixels.getPixel(x, y) == 0xAA00FF) {
						createCheckpoint(x, y);
					}
					if( pixels.getPixel(x, y) == 0x7F007F) {
						if(levelNumber == 4) {
							boss = new Boss(x*8, y*8 - 20, this);
							add(boss);
						}
					}
				}
			}
			
			if(checkPointReached)
				player = new Player(checkPointXY.x, checkPointXY.y - 20);
				
			//Camera setup
			FlxG.camera.follow(player, FlxCamera.STYLE_PLATFORMER);
			level.follow();
			
			if (Game.DEBUG) {
//				var testRun:RunSprite = new RunSprite();
//				testRun.x = 200;
//				testRun.y = 192;
//				add(testRun);
			}
			add(exit);
			add(checkPoint);
			add(player);
			add(downers);
			add(lines);
			add(jumpPads);
			add(needles);

			//heart rate monitor
			heartRate = new HeartRateMonitor(10, 10, 100, 50, this.add, this.remove);
			heartRate.scrollFactor.x = heartRate.scrollFactor.y = 0; 
			heartRate.start();
			this.add(heartRate);
			
			//ambient noise
			startRandomDowntownNoise();
			
			//other noise
			sniffSound = new FlxSound();
			sniffSound.loadEmbedded(Resource.SOUND_SNIFF);
			downerSound = new FlxSound();
			downerSound.loadEmbedded(Resource.SOUND_DOWNER);
			jumpSound = new FlxSound();
			jumpSound.loadEmbedded(Resource.SOUND_JUMP);
			springSound = new FlxSound();
			springSound.loadEmbedded(Resource.SOUND_SPRING);
			
			//fade in
			FlxG.flash(0xff000000, 2);
		}
		
		private function startRandomDowntownNoise():void
		{
			if (downtownNoise)
			{
				downtownNoise.stop();
				downtownNoise = null;
			}
			
			downtownNoise = new FlxSound();
			var rand:int = Math.floor(Math.random() * 3);
			switch (rand)
			{
				case 0:
					downtownNoise.loadEmbedded(Resource.SOUND_DOWNTOWN_1, true);
					downtownString = "downtown 1";
					break;
				case 1:
					downtownNoise.loadEmbedded(Resource.SOUND_DOWNTOWN_2, true);
					downtownString = "downtown 2";
					break;
				case 2:
					downtownNoise.loadEmbedded(Resource.SOUND_DOWNTOWN_3, true);
					downtownString = "downtown 3";
					break;
				default:
					downtownNoise.loadEmbedded(Resource.SOUND_DOWNTOWN_1, true);
					downtownString = "downtown 1";
					break;
			}
			downtownNoise.volume = 1;
			downtownNoise.fadeIn(5);
		}
		
		public function getLine(Line:FlxSprite, player:Player):void {
			Line.kill();
			player.takeBump(Player.BUMP_SIZE);
			makePowder(5, 10);
			sniffSound.play();
		}
		
		private function makePowder(min:int, max:int, point:FlxPoint=null):void
		{
			var emitter:FlxEmitter = new FlxEmitter();
			if (point)
			{
				emitter.x = point.x;
				emitter.y = point.y;
			}
			else
			{
				emitter.at(player);
			}
			emitter.minParticleSpeed = new FlxPoint(-10, -10);
			emitter.maxParticleSpeed = new FlxPoint(10, 10);
			var particleNum:int = Math.floor(Math.random() * (1 + max - min)) + min;
			for(var i:int = 0; i < particleNum; i++)
			{
				var particle:FlxParticle = new FadingParticle();
				particle.makeGraphic(2, 2, 0xffffffff);
				particle.exists = false;
				particle.solid = false;
				particle.drag.x = particle.drag.y = 0;
				emitter.add(particle);
			}
			add(emitter);
			emitter.start();
		}
		
		public function getDowner(Downer:FlxSprite, player:Player):void {
			Downer.kill();
			player.comeDown();
			downerSound.play();
		} 
		
		public function hitJumpPad(pad:FlxSprite, player:Player):void {
			pad.play("spring");
			springSound.play();
			if (player.alive) {
				player.velocity.y = -player.jumpStrength * 1.7;
			}
		}
		
		public function hitExit(exit:FlxSprite, player:Player):void {
			if (player.alive) {
				player.control = false;
				player.kill();
				if (Game.DEBUG)
				{
					exitText = new FlxText(FlxG.camera.width/6, FlxG.camera.height/3, 250);
					exitText.scrollFactor.x = exitText.scrollFactor.y = 0;
					exitText.text = "Thats enough blow for one day, now sleep.";
					exitText.size = 15;
					add(exitText);
				}
				exit.play("goThrough");
				FlxG.fade(0xff000000, 3, startNextLevel);
			}
		}
		
		public function hitNeedles(needle:FlxSprite = null, p:Player = null):void {
			if (p.alive) {
				killedByNeedles = true;
				hitKiller(needle, p);
				makePowder(20, 100);
				sniffSound.play();
			}
		}
		
		public function hitKiller(killer:Object, player:Player):void {
			if (player.alive) {
				if (Game.DEBUG) {
					howIDiedText.text = "You deaded by: ";
					if (killer != null) {
						howIDiedText.text += killer + "!";
					}
				}
				player.needsToDie = true;
				player.control = false;
			}
		}
		
		public function getCheckPoint(check:FlxSprite, player:Player):void {
			if (!checkPointReached) {
			(check as Checkpoint).raiseFlag();
			checkPointReached = true;
			}
		}
		
		public function createDowner(X:uint, Y:uint):void {
			downers.add(new Downer(X, Y));
		}
		
		public function createCheckpoint(X:uint, Y:uint):void {
			var point:Checkpoint = new Checkpoint(X, Y);
			if (checkPointReached) {
				checkPointXY = new Point(X*  8, Y * 8);
				point.play("up");
			}
			checkPoint.add(point); 
		}
		
		public function createLine(X:uint,Y:uint):void {
			lines.add(new Line(X, Y));
		}
		
		public function createJumpPad(X:uint, Y:uint):void {
			jumpPads.add(new Spring(X, Y));
		}
		
		public function createExit(X:uint, Y:uint):void {
			exit.add(new Exit(X, Y));
		}
		
		public function createNeedle(X:uint, Y:uint):void {
			var needle:FlxSprite = new FlxSprite(X*8, Y*8 + 4);
			needle.allowCollisions = FlxObject.UP;
			needle.immovable = true;
			needle.makeGraphic(8, 4, 0xFFFFFFFF);
			needles.add(needle);
		}
		
		public function startNextLevel():void {
			heartRate.stop();
			downtownNoise.stop();
			if (levelNumber == 4)
				FlxG.switchState(new MainMenuState(false));
			else
				FlxG.switchState(new PlayState(levelNumber + 1, false));
		}
		
		// Main game loop.
		override public function update():void
		{
			if (Game.DEBUG)
			{
				debugText.text = "(" + player.x.toFixed(0) + ", " + player.y.toFixed(0) + ")\n" + 
								"Speed:\n x = "  + player.velocity.x.toFixed(0) + "\n y = " + player.velocity.y.toFixed(0)
								+ "\n" + downtownString;
			}
			
			if (player.control) {
			player.acceleration.x = 0;
			
			// handle input
				if(FlxG.keys.LEFT)
					player.acceleration.x = -player.accelSpeed;
				if(FlxG.keys.RIGHT)
					player.acceleration.x = player.accelSpeed;
				if(FlxG.keys.justPressed("SPACE") && player.isTouching(FlxObject.FLOOR)) {
					jumpSound.play();
					player.velocity.y = -player.jumpStrength;
				}
				
				// Update animation
				if (!player.isTouching(FlxObject.FLOOR)) {
					player.play("jump");
				}
				else {
					if (player.velocity.x < 0) {
						player.play("runLeft");
					} else if (player.velocity.x > 0) {
						player.play("runRight");
					} else {
						player.play("idle");
					}
				}
			}
			
			if(boss)
				boss.update();
			
			if(FlxG.keys.T) {
				debugText.visible = !debugText.visible;
			}
			
			if (Game.DEBUG && FlxG.keys.S) {
				startNextLevel();
			}
			
			if (player.alive && player.needsToDie && player.isTouching(FlxObject.FLOOR)) {			
				if (Game.DEBUG)
				{
					deathText = new FlxText(FlxG.camera.width/6, FlxG.camera.height/3, 250);
					deathText.scrollFactor.x = deathText.scrollFactor.y = 0;
					if (killedByNeedles)
						deathText.text = "Get SPIKED";
					else if(player.heartRate > 200)
						deathText.text = "You got too fucking high!";
					else if(player.heartRate < 60)
						deathText.text = "Keep your heart rate up!";
					deathText.text += '\n' + "    'space' to restart.";
					deathText.size = 15;
					add(deathText);
				}
				
				heartRate.stop(4);
				
				FlxG.fade(0xff000000, 4, restartLevel);
				
				player.die(killedByNeedles);
			}
			
			super.update();
			
			FlxG.overlap(lines, player, getLine);
			
			FlxG.overlap(downers, player, getDowner);
			
			FlxG.collide(jumpPads, player, hitJumpPad);
			
			FlxG.overlap(exit, player, hitExit);
			
			FlxG.collide(needles, player, hitNeedles);
			
			FlxG.collide(checkPoint, player, getCheckPoint);
			
			FlxG.collide(level, player);
			
			if(boss)
				FlxG.collide(level, boss);
			
			if (Game.DEBUG)
			{
				heartRateText.text = "Heart Rate:" + '\n' + int(player.heartRate);
			}
			
			if(player.control) {
				player.decay();
				
				heartRate.setBPM(player.heartRate);
				heartRate.update();
				
				if(player.heartRate < Player.MIN_HEART_RATE)
					hitKiller("slowHeart" , player);
				else if(player.heartRate > Player.MAX_HEART_RATE)
					hitKiller("fastHeart", player);
			}
				
 		}
		
		private function restartLevel():void
		{
				downtownNoise.stop();
				heartRate.stop();				
			if (levelNumber == 4 && player.x > 550) {
				startNextLevel();
			}
			else {
				FlxG.switchState(new PlayState(levelNumber, checkPointReached));
			}
		}
		
		private function deathPowderCallback():void
		{
			makePowder(20, 100, new FlxPoint(player.x + 15, player.y + 13));
		}
	}
}
